﻿package kliment.math {
	import kliment.utils._String;

	/**
	* ...
	* @author	Kliment
	* @version	1.9.63
	*/
	public class Calc {
		static public const RM_ROUND:String = "round";
		static public const RM_CEIL:String = "ceil";
		static public const RM_FLOOR:String = "floor";

		static public function getPercent(part:Number, total:Number):Number {
			return part / total * 100;
		}

		static public function getFromPercent(percent:Number, total:Number):Number {
			return total * .01 * percent;
		}

		static public function randomRange(min:Number, max:Number):Number {
			return Math.floor(Math.random() * (max - min + 1)) + min;
		}

		static public function sqr(value:Number):Number {
			return value * value;
		}

		static public function exponentiation(value:Number, exponent:Number):Number {
			return Math.exp(Math.log(Math.abs(value)) * exponent);
		}

		static public function roundDec(value:Number, dec:Number):Number {
			var multiplier:Number = Math.pow(10, dec || 0);
			return Math.round((value || 0) * multiplier) / multiplier;
		}

		static public function inInterval(min:Number, max:Number, value:Number):Number {
			return Math.max(min, Math.min(value, max));
		}

		static public function valueFromInterval(start:Number, stop:Number, current_step:Number, count_step:Number, lawFunction:Function):Number {
			return start + lawFunction(current_step, 0, stop - start, count_step);
		}

		static public function inIntervalRound(min:Number, max:Number, value:Number):Number {
			if (value > max)
				return min;
			else if (value < min)
				return max;
			return value;
		}

		static public function intervalRoundNum(min:Number, max:Number, center:Number, value:Number):Number {
			var length:Number = Math.abs(max - min);
			var evenLenght:Number = Number(even(length));
			var halfLength:Number = Math.round(half(length));
			var result:Number = value - center;
			if (result > halfLength)
				result = result - halfLength * 2 - evenLenght;
			else if (result < -halfLength)
				result = result + halfLength * 2 + evenLenght;
			return result;
		}

		static public function even(value:Number):Boolean {
			return !odd(value);
		}

		static public function odd(value:Number):Boolean {
			return Boolean(value % 2);
		}

		/**
		 * Returns decimal part.
		 * @param	value Target number
		 * @param	length Length of the decimal part.
		 *  @return
		 */
		static public function decimalPart(value:Number, length:uint):Number {
			var valueStr:String = String(value);
			var dotIndex:Number = valueStr.indexOf('.');
			if (dotIndex < 0)
				return 0;
			var result:Number = Number('0' + valueStr.substring(dotIndex, valueStr.length));
			if (length > 0) {
				var m:Number = Number('1' + _String.repeat('0', length));
				result = Math.round(result * m) / m;
			}
			if (String(result).length < length)
				result = Number('0' + valueStr.substring(dotIndex, valueStr.length).substring(0, length + 1));
			return result;
		}

		/**
		 * Return amount of seconds
		 * @param	bytesLoaded
		 * @param	bytesTotal
		 * @param	timeStart
		 * @param	timeNow
		 * @return	Amount of seconds
		 */
		static public function timeLeft(bytesLoaded:Number, bytesTotal:Number, secondsStart:Number, secondsNow:Number):Number {
			return (bytesTotal - bytesLoaded) / (bytesLoaded / ((secondsNow - secondsStart) * .001));
		}

		static public function ctg(radian:Number):Number {
			return Math.cos(radian) / Math.sin(radian);
		}

		static public function sec(radian:Number):Number {
			return 1 / Math.cos(radian);
		}

		static public function cosec(radian:Number):Number {
			return 1 / Math.sin(radian);
		}

		static public function half(value:Number, funct:Function = null):Number {
			value *= .5;
			if (funct != null)
				value = funct(value)
			return value;
		}

		static public function deltaRatio(new_value:Number, value:Number, rate:Number):Number {
			return (new_value - value) * rate;
		}

		static public function deltaRatioResult(new_value:Number, value:Number, rate:Number):Number {
			return value + deltaRatio(new_value, value, rate);
		}

		static public function round(value:Number, functionName:String, count:int):Number {
			var funct:Function = Math[functionName];
			if (funct is Function)
				null;
			else
				return value;
			var val:String = "1";
			for(var i:int = 0; i<count; i++)
				val += "0";
			var fix:int = int(val);
			return Number(funct(value*fix)/fix);
		}

		static public function mapRange(input:Number, input_min:Number, input_max:Number, output_min:Number, output_max:Number):Number {
			return output_min + (input - input_min) * ((output_max - output_min) / (input_max - input_min));
		}

		static public function nearestValueIndex(numbers:Array, value:Number, vector:Boolean, fromIndex:uint = 0):Number {
			var result:Number;
			var dx:Number;
			var minDx:Number = Number.MAX_VALUE;
			var ix:Number;
			var length:uint = numbers.length;
			for (var i:uint = fromIndex; i < length; ++i ) {
				ix = numbers[i];
				dx = Math.abs(value - ix);
				if (dx <= minDx) {
					result = i;
					minDx = dx;
				}
				if (vector && dx > minDx)
					break;
			}
			return result;
		}

		static public function nearestValue(numbers:Array, value:Number, vector:Boolean, fromIndex:uint = 0):Number {
			return numbers[nearestValueIndex(numbers, value, vector, fromIndex)];
		}
	}
}